﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Runtime;
using System.Runtime.InteropServices;
using System.Text;
using System.Windows.Forms;

using Storm;
using Storm.Docking;
using Storm.Docking.Controls;
using Storm.Docking.Controls.Float;
using Storm.Docking.Visual;
using Storm.Docking.Visual.Drawing;
using Storm.Docking.Visual.Glyphs;

namespace Storm.Docking.Controls.Float
{
    /// <summary>
    /// Possible results of a hit test on the non client area of a form.
    /// </summary>
    public enum NonClientHitTestResult
    {
        Nowhere = 0,
        Client = 1,
        Caption = 2,
        GrowBox = 4,
        MinimizeButton = 8,
        MaximizeButton = 9,
        Left = 10,
        Right = 11,
        Top = 12,
        TopLeft = 13,
        TopRight = 14,
        Bottom = 15,
        BottomLeft = 16,
        BottomRight = 17
    }

    /// <summary>
    /// Enum describing what direction the user is currently resizing towards.
    /// </summary>
    [Flags()]
    public enum ResizeDirection
    {
        None = 0,
        Left = 1,
        TopLeft = 2,
        Top = 4,
        TopRight = 8,
        Right = 16,
        BottomRight = 32,
        Bottom = 64,
        BottomLeft = 128
    }

    [StructLayout(LayoutKind.Sequential)]
    public struct RECT
    {
        public int Left;
        public int Top;
        public int Right;
        public int Bottom;
    }

    [StructLayout(LayoutKind.Sequential)]
    public struct NCCALCSIZEPARAMS
    {
        public RECT rect0;
        public RECT rect1;
        public RECT rect2;
        public IntPtr lppos;
    }

    /// <summary>
    /// Represents a generic form that is owner drawn.
    /// </summary>
    public partial class DockForm : Form
    {
        #region Fields

        // Const members.
        private const int BorderWidth = 6;

        private Padding margins;
        private bool marginsChecked = false;

        // This member draws our DockForm.
        private DockRenderer renderer = null;

        private bool allowMoving = true;
        private ResizeDirection resizeDir = ResizeDirection.None;
        private ResizeDirection allowResizeFlags = ResizeDirection.Bottom |
                                            ResizeDirection.BottomLeft |
                                            ResizeDirection.BottomRight |
                                            ResizeDirection.Left |
                                            ResizeDirection.Right |
                                            ResizeDirection.Top |
                                            ResizeDirection.TopLeft |
                                            ResizeDirection.TopRight;

        #endregion

        #region Win32

        [DllImport("user32.dll")]
        public static extern bool ReleaseCapture();

        [DllImport("user32.dll")]
        public static extern int SendMessage(IntPtr hWnd,
            int Msg, int wParam, int lParam);

        private const int WMNCCALCSIZE    = 0x83;
        private const int WMNCLBUTTONDOWN = 0xa1;
        private const int WMNCLBUTTONUP   = 0x00A2;

        private const int HTBORDER      = 18;
        private const int HTBOTTOM      = 15;
        private const int HTBOTTOMLEFT  = 16;
        private const int HTBOTTOMRIGHT = 17;
        private const int HTCAPTION     = 2;
        private const int HTLEFT        = 10;
        private const int HTRIGHT       = 11;
        private const int HTTOP         = 12;
        private const int HTTOPLEFT     = 13;
        private const int HTTOPRIGHT    = 14;

        #endregion

        #region Properties

        /// <summary>
        /// Gets or sets the DockRenderer that will render the DockForm.
        /// </summary>
        public DockRenderer Renderer
        {
            get { return renderer; }
            set
            {
                renderer = value;

				this.BackColor = renderer.DockColorTable.FormBodyColor;

                this.Invalidate();
            }
        }

        /// <summary>
        /// Gets the margins of the non-client area.
        /// </summary>
        public Padding Margins
        { get { return margins; } }

        /// <summary>
        /// Gets or sets if the margins are already checked by WndProc.
        /// </summary>
        private bool MarginsChecked
        {
            get { return marginsChecked; }
            set { marginsChecked = value; }
        }

        /// <summary>
        /// Gets or sets whether the user should be able to drag-move the Form around.
        /// </summary>
        public bool AllowMoving
        {
            get { return allowMoving; }
            set { allowMoving = value; }
        }

        /// <summary>
        /// Gets or sets what directions the Form may resize to.
        /// </summary>
        public ResizeDirection AllowResizeFlags
        {
            get { return allowResizeFlags; }
            set { allowResizeFlags = value; }
        }

        /// <summary>
        /// Gets or sets what direction the Form is currently resizing to.
        /// </summary>
        private ResizeDirection ResizeDirection
        {
            get { return resizeDir; }
            set
            {
                resizeDir = value;

                // BUG FIX: somehow the Controls in the Form's cursor, if it was 
                // Default, wasn't reset when we hovered over it from the edge, so
                // this fixes it.
                List<Control> controlList = new List<Control>();
                foreach (Control control in Controls)
                {
                    if (control.Cursor == Cursors.Default)
                        controlList.Add(control);
                }

                Cursor = Cursors.Default;
                switch (value)
                {
                    case ResizeDirection.Left:
                        Cursor = Cursors.SizeWE;
                        break;
                    case ResizeDirection.Right:
                        Cursor = Cursors.SizeWE;
                        break;
                    case ResizeDirection.Top:
                        Cursor = Cursors.SizeNS;
                        break;
                    case ResizeDirection.Bottom:
                        Cursor = Cursors.SizeNS;
                        break;
                    case ResizeDirection.BottomLeft:
                        Cursor = Cursors.SizeNESW;
                        break;
                    case ResizeDirection.TopRight:
                        Cursor = Cursors.SizeNESW;
                        break;
                    case ResizeDirection.BottomRight:
                        Cursor = Cursors.SizeNWSE;
                        break;
                    case ResizeDirection.TopLeft:
                        Cursor = Cursors.SizeNWSE;
                        break;
                    default:
                        Cursor = Cursors.Default;
                        break;
                }

                foreach (Control control in controlList)
                    control.Cursor = Cursors.Default;
            }
        }

        #endregion

        #region Methods

        #region Internal

        internal void MoveControl(IntPtr hWnd)
        {
            if (WindowState == FormWindowState.Normal)
            {
                ReleaseCapture();
                SendMessage(hWnd, WMNCLBUTTONDOWN, HTCAPTION, 0);
            }
        }

        #endregion

        #region Private

        /// <summary>
        /// Sets the value of the <see cref="Margins"/> property.
        /// </summary>
        /// <param name="p"></param>
        private void SetMargins(Padding p)
        {
            margins = p;

            Padding formPadding = p;
            formPadding.Top = p.Bottom - 1;

            if (!DesignMode)
                Padding = formPadding;
        }

        private void ResizeControl(IntPtr hWnd, ResizeDirection direction)
        {
            if (WindowState == FormWindowState.Normal)
            {
                int dir = -1;
                switch (direction)
                {
                    case ResizeDirection.Left:
                        dir = HTLEFT;
                        break;
                    case ResizeDirection.TopLeft:
                        dir = HTTOPLEFT;
                        break;
                    case ResizeDirection.Top:
                        dir = HTTOP;
                        break;
                    case ResizeDirection.TopRight:
                        dir = HTTOPRIGHT;
                        break;
                    case ResizeDirection.Right:
                        dir = HTRIGHT;
                        break;
                    case ResizeDirection.BottomRight:
                        dir = HTBOTTOMRIGHT;
                        break;
                    case ResizeDirection.Bottom:
                        dir = HTBOTTOM;
                        break;
                    case ResizeDirection.BottomLeft:
                        dir = HTBOTTOMLEFT;
                        break;
                }

                if (dir != -1)
                {
                    ReleaseCapture();
                    SendMessage(hWnd, WMNCLBUTTONDOWN, dir, 0);
                }
            }
        }

        #endregion

        #region Protected

		protected override void OnControlAdded(ControlEventArgs e)
		{
			if (e.Control is DockPanel)
			{
				(e.Control as DockPanel).DockForm = this;
				this.Renderer = (e.Control as DockPanel).Renderer;
			}

			base.OnControlAdded(e);
		}

        protected override void WndProc(ref Message m)
        {
            Rectangle rect = new Rectangle(0, 0, Width, 
                17);

            if (m.Msg == WMNCCALCSIZE &&
                (int)m.WParam == 1)
            {
                NCCALCSIZEPARAMS nccsp = (NCCALCSIZEPARAMS)
                Marshal.PtrToStructure(m.LParam, typeof(NCCALCSIZEPARAMS));

                if (MarginsChecked == false)
                {
                    SetMargins(new Padding(
                        nccsp.rect2.Left - nccsp.rect1.Left,
                        nccsp.rect2.Top - nccsp.rect1.Top,
                        nccsp.rect1.Right - nccsp.rect2.Right,
                        nccsp.rect1.Bottom - nccsp.rect2.Bottom));

                    MarginsChecked = true;
                }

                Marshal.StructureToPtr(nccsp, m.LParam, false);
                m.Result = IntPtr.Zero;
            }

            base.WndProc(ref m);
        }

        protected override void OnPaint(PaintEventArgs e)
        {
            this.Renderer.FormDrawForm(this, e.Graphics);
            base.OnPaint(e);
        }

        protected override void OnResize(EventArgs e)
        {
            if (WindowState == FormWindowState.Maximized)
                Cursor = Cursors.Default;

            base.OnResize(e);
        }

        protected override void OnMouseDown(MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Left)
            {
                if (ResizeDirection == ResizeDirection.None)
                {
                    if (this.AllowMoving)
                        MoveControl(Handle);
                }
                else
                {
                    if ((this.AllowResizeFlags & ResizeDirection) == ResizeDirection)
                        ResizeControl(Handle, ResizeDirection);
                }
            }

            base.OnMouseDown(e);
        }

        protected override void OnMouseMove(MouseEventArgs e)
        {
            base.OnMouseMove(e);

            if (WindowState == FormWindowState.Normal)
            {
                if (e.Location.X < BorderWidth & e.Location.Y < BorderWidth)
                {
                    if ((this.AllowResizeFlags & ResizeDirection.TopLeft) ==
                                ResizeDirection.TopLeft)
                    {
                        ResizeDirection = ResizeDirection.TopLeft;
                    }
                }
                else if (e.Location.X < BorderWidth & e.Location.Y > Height - BorderWidth)
                {
                    if ((this.AllowResizeFlags & ResizeDirection.BottomLeft) ==
                                ResizeDirection.BottomLeft)
                    {
                        ResizeDirection = ResizeDirection.BottomLeft;
                    }
                }
                else if (e.Location.X > Width - BorderWidth &
                    e.Location.Y > Height - BorderWidth)
                {
                    if ((this.AllowResizeFlags & ResizeDirection.BottomRight) ==
                                ResizeDirection.BottomRight)
                    {
                        ResizeDirection = ResizeDirection.BottomRight;
                    }
                }
                else if (e.Location.X > Width - BorderWidth & e.Location.Y < BorderWidth)
                {
                    if ((this.AllowResizeFlags & ResizeDirection.TopRight) ==
                                ResizeDirection.TopRight)
                    {
                        ResizeDirection = ResizeDirection.TopRight;
                    }
                }
                else if (e.Location.X < BorderWidth)
                {
                    if ((this.AllowResizeFlags & ResizeDirection.Left) ==
                                ResizeDirection.Left)
                    {
                        ResizeDirection = ResizeDirection.Left;
                    }
                }
                else if (e.Location.X > Width - BorderWidth)
                {
                    if ((this.AllowResizeFlags & ResizeDirection.Right) ==
                                ResizeDirection.Right)
                    {
                        ResizeDirection = ResizeDirection.Right;
                    }
                }
                else if (e.Location.Y < BorderWidth)
                {
                    if ((this.AllowResizeFlags & ResizeDirection.Top) ==
                                ResizeDirection.Top)
                    {
                        ResizeDirection = ResizeDirection.Top;
                    }
                }
                else if (e.Location.Y > Height - BorderWidth)
                {
                    if ((this.AllowResizeFlags & ResizeDirection.Bottom) ==
                                ResizeDirection.Bottom)
                    {
                        ResizeDirection = ResizeDirection.Bottom;
                    }
                }
                else
                    ResizeDirection = ResizeDirection.None;
            }
        }

        #endregion

        #endregion

		/// <summary>
		/// Initializes a new instance of the <see cref="DockForm"/> class.
		/// </summary>
        public DockForm()
        {
            // Initialize our DockRenderer.
            this.Renderer = new DockRenderer();

            // Required by Windows Forms Designer.
            this.InitializeComponent();

            this.SetStyle(ControlStyles.ResizeRedraw, true);
            this.SetStyle(ControlStyles.AllPaintingInWmPaint, true);
            this.SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
            this.SetStyle(ControlStyles.UserPaint, true);
            this.SetStyle(ControlStyles.Opaque, true);
			this.SetStyle(ControlStyles.SupportsTransparentBackColor, true);
        }
    }
}
